#ifndef __ADVENTURE_DATA_STRUCTURES_HH__
#define __ADVENTURE_DATA_STRUCTURES_HH__
#include <inttypes.h>
#include <map>
#include <set>
#include <string>
#include <vector>

#define POS_NOWHERE -1
#define POS_KILLED -2 //Also complete destroyed for objects
#define POS_CARRIED -3 //Object is being carried
#define POS_HERE -4 //Is object here or move it here!

#define EVENT_NOTP -1 //Must not be present!
#define EVENT_ANY -2 //Anything, but must be present.
#define EVENT_MAYBE -3 //Anything, may even be missing.
#define EVENT_ILLEGAL -32768 //This is illegal!

//! this is an array of pcode
typedef std::vector<unsigned short> pcode_array;

//! data type for mapping from verb strings to verb numbers
typedef std::map<std::string,int> VerbMap;
//! data type for mapping from adjective strings to adjective numbers
typedef std::map<std::string,int> AdjectiveMap;

/*! \brief An in-game object.
 *
 * Game objects are the bread and butter of adventures. Here we store
 * all important information about them. The position is where the
 * object is located (see defines above), the weight, a name, and a
 * description.
 */

struct Object {
  int pos; //!< the initial position of the object
  int weight; //!< if <0 then object can not be carried
  std::string name; //!< the name of the object to recognise
  std::string desc; //!< a description to print
  int adjective; //!< the corresponding adjective if multiple objects...

  Object();
  Object(int p, int w, const std::string& n, const std::string& d, int a = -1);
};
typedef std::vector<Object> ObjectArray;


struct Cmd {
  unsigned int line;
  long c;
  int p1, p2;

  Cmd(unsigned int line_, long i = -1, int p1_ = -1, int p2_ = -1);
  Cmd(long i = -1, int p1_ = -1, int p2_ = -1);
};

/*!
 * if verb, adverb, ... == -1 then it must not be present!
 * if                   == -2 then it must be present but can be anything
 */
struct Event {
  int location;
  int verb;
  int adverb;
  int obj1;
  int adj1;
  int obj2;
  int adj2;
  std::vector<Cmd> conditions;
  std::vector<Cmd> actions;
  pcode_array pcode;

  Event() : location(EVENT_ILLEGAL), verb(EVENT_ILLEGAL), adverb(EVENT_ILLEGAL), obj1(EVENT_ILLEGAL), adj1(EVENT_ILLEGAL), obj2(EVENT_ILLEGAL), adj2(EVENT_ILLEGAL) {}
  Event(const std::vector<Cmd> &act, int l, int v, int av = -1, int o1 = -1, int a1 = -1, int o2 = -1, int a2 = -1) : location(l), verb(v), adverb(av), obj1(o1), adj1(a1), obj2(o2), adj2(a2), actions(act) {}
  Event(int l, int v, int av, int o1, int a1, int o2, int a2, const std::vector<Cmd> &cond, const std::vector<Cmd> &act) : location(l), verb(v), adverb(av), obj1(o1), adj1(a1), obj2(o2), adj2(a2), conditions(cond), actions(act) {}
};


typedef std::map<int, int> ExitMap;
struct Location {
  std::string desc_short;
  std::string desc_long;
  //does not work... map<int,int> exits2; //Verb->room
  //std::vector<VerbExit> exits;
  ExitMap exits; //Map verb to room number
  bool visited;
};
typedef std::vector<Location> LocationArray;


/*! \brief current adventure state
 *
 * This structure holds the complete current state of the adventure.
 */
struct Adventure {
  unsigned int turns; //!< number of turns so far
  int here; //!< position of the player
  bool end; //!< game ends?
  bool dead; //!< player dead?
  int verb;
  int adverb;
  int obj1;
  int adj1;
  int obj2;
  int adj2;

  ObjectArray objects;
  VerbMap verbs;
  std::map<int,std::string> movement_verbs; //!< These are checked for printing.
  LocationArray locations;
  std::vector<Event> events;
  std::vector<pcode_array> hp_events; //!< High priority events, handled before input...
  std::vector<std::string> msgs;
  std::vector<bool> switches;
  /*! \brief counters for the game
   *
   * There are 32767 counters available... should be enough for
   * everybody.  Each counter can store a value between -32767 and
   * 32767. (-32768 means that the counter is unsused and it will not
   * be written to disk.
   *
   * Counter 32767 stores the number of points the player has earned
   * so far.
   */
  std::vector<int> counters;
  AdjectiveMap adjectives;

  Adventure();

  /*! \brief find an object by name
   *
   * Find an object in the list of objects by name. The object must be
   * present in the current location or being carried. Otherwise -1 is
   * returned.
   *
   * If an adjective is given it must match too. If adjective is -1
   * and *only* a single object is found this one is returned.
   *
   * \param obj object name
   * \param adjective corresponding adjective
   * \return object id or -1
   */
  int find_object(const std::string &obj, int adjective);

  /*! \brief inventory string
   *
   * Get the inventory as a string.
   *
   * \return string
   */
  std::string inventory() const;

  std::string get_object_name(int i) const;
  std::string to_json(void) const;

};


#endif
